aviutl2\generic\binding/
host_app.rs

1use crate::{AviUtl2Info, generic::EditSection};
2use pastey::paste;
3
4/// ホストアプリケーションのハンドル。
5/// プラグインの初期化処理で使用します。
6///
7/// # Panics
8///
9/// この方がプラグインの初期化処理の外で使用された場合はPanicします。
10pub struct HostAppHandle<'a> {
11    internal: *mut aviutl2_sys::plugin2::HOST_APP_TABLE,
12    global_leak_manager: &'a mut crate::common::LeakManager,
13    kill_switch: std::sync::Arc<std::sync::atomic::AtomicBool>,
14    plugin_registry: &'a mut crate::generic::PluginRegistry,
15}
16
17/// プラグインの初期化状態を管理するためのハンドル。
18pub struct SubPlugin<T> {
19    plugin: std::marker::PhantomData<T>,
20    internal: std::sync::Arc<InternalReferenceHandle>,
21}
22struct InternalReferenceHandle {
23    uninitialize_fn: fn(),
24}
25impl Drop for InternalReferenceHandle {
26    fn drop(&mut self) {
27        (self.uninitialize_fn)();
28    }
29}
30
31impl<'a> HostAppHandle<'a> {
32    pub(crate) unsafe fn new(
33        internal: *mut aviutl2_sys::plugin2::HOST_APP_TABLE,
34        global_leak_manager: &'a mut crate::common::LeakManager,
35        kill_switch: std::sync::Arc<std::sync::atomic::AtomicBool>,
36        plugin_registry: &'a mut crate::generic::PluginRegistry,
37    ) -> Self {
38        Self {
39            internal,
40            global_leak_manager,
41            kill_switch,
42            plugin_registry,
43        }
44    }
45
46    fn assert_not_killed(&self) {
47        if self.kill_switch.load(std::sync::atomic::Ordering::SeqCst) {
48            panic!("This HostAppHandle is no longer valid.");
49        }
50    }
51
52    /// プラグインの情報を設定します。
53    /// 「プラグイン情報」ダイアログで表示されます。
54    pub fn set_plugin_information(&mut self, information: &str) {
55        self.assert_not_killed();
56        let information = if cfg!(debug_assertions) {
57            format!("{information} (Debug Build)")
58        } else {
59            information.to_string()
60        };
61        unsafe {
62            ((*self.internal).set_plugin_information)(
63                self.global_leak_manager.leak_as_wide_string(&information),
64            )
65        }
66    }
67
68    /// プロジェクトデータ編集用のハンドルを登録します。
69    pub fn create_edit_handle(&mut self) -> crate::generic::EditHandle {
70        self.assert_not_killed();
71        let raw_handle = unsafe { ((*self.internal).create_edit_handle)() };
72        unsafe { crate::generic::EditHandle::new(raw_handle) }
73    }
74
75    /// インポートメニューを登録します。
76    ///
77    /// # See Also
78    ///
79    /// - [`crate::generic::menus`]
80    pub fn register_import_menu(
81        &mut self,
82        name: &str,
83        callback: extern "C" fn(*mut aviutl2_sys::plugin2::EDIT_SECTION),
84    ) {
85        self.assert_not_killed();
86        unsafe {
87            ((*self.internal).register_import_menu)(
88                self.global_leak_manager.leak_as_wide_string(name),
89                callback,
90            )
91        }
92    }
93
94    /// エクスポートメニューを登録します。
95    ///
96    /// # See Also
97    ///
98    /// - [`crate::generic::menus`]
99    pub fn register_export_menu(
100        &mut self,
101        name: &str,
102        callback: extern "C" fn(*mut aviutl2_sys::plugin2::EDIT_SECTION),
103    ) {
104        self.assert_not_killed();
105        unsafe {
106            ((*self.internal).register_export_menu)(
107                self.global_leak_manager.leak_as_wide_string(name),
108                callback,
109            )
110        }
111    }
112
113    /// レイヤーメニューを登録します。
114    /// レイヤー編集でオブジェクト未選択時の右クリックメニューに追加されます。
115    ///
116    /// # See Also
117    ///
118    /// - [`crate::generic::menus`]
119    pub fn register_layer_menu(
120        &mut self,
121        name: &str,
122        callback: extern "C" fn(*mut aviutl2_sys::plugin2::EDIT_SECTION),
123    ) {
124        self.assert_not_killed();
125        unsafe {
126            ((*self.internal).register_layer_menu)(
127                self.global_leak_manager.leak_as_wide_string(name),
128                callback,
129            )
130        }
131    }
132
133    /// オブジェクトメニューを登録します。
134    /// レイヤー編集でオブジェクト選択時の右クリックメニューに追加されます。
135    ///
136    /// # See Also
137    ///
138    /// - [`crate::generic::menus`]
139    pub fn register_object_menu(
140        &mut self,
141        name: &str,
142        callback: extern "C" fn(*mut aviutl2_sys::plugin2::EDIT_SECTION),
143    ) {
144        self.assert_not_killed();
145        unsafe {
146            ((*self.internal).register_object_menu)(
147                self.global_leak_manager.leak_as_wide_string(name),
148                callback,
149            )
150        }
151    }
152
153    /// ウィンドウクライアントを登録します。
154    ///
155    /// # Panics
156    ///
157    /// Win32のウィンドウハンドル以外が渡された場合はPanicします。
158    pub fn register_window_client<T: raw_window_handle::HasWindowHandle>(
159        &mut self,
160        name: &str,
161        instance: &T,
162    ) -> Result<(), raw_window_handle::HandleError> {
163        self.assert_not_killed();
164        let raw_handle = instance.window_handle()?;
165        let hwnd = match raw_handle.as_raw() {
166            raw_window_handle::RawWindowHandle::Win32(handle) => handle.hwnd,
167            _ => panic!("Only Win32WindowHandle is supported"),
168        };
169        unsafe {
170            ((*self.internal).register_window_client)(
171                self.global_leak_manager.leak_as_wide_string(name),
172                hwnd.get() as *mut std::ffi::c_void,
173            );
174        }
175        Ok(())
176    }
177
178    /// メニューを一括登録します。
179    ///
180    /// # See Also
181    ///
182    /// - [`crate::generic::menus`]
183    pub fn register_menus<T: GenericPluginMenus>(&mut self) {
184        self.assert_not_killed();
185        T::register_menus(self);
186    }
187
188    /// プロジェクトファイルをロードした直後に呼ばれる関数を登録します。
189    /// また、プロジェクトの初期化時にも呼ばれます。
190    ///
191    /// # Note
192    ///
193    /// [`crate::generic::GenericPlugin::on_project_load`] が自動的に登録されるため、
194    /// 通常はこの関数を直接使用する必要はありません。
195    pub fn register_project_load_handler(
196        &mut self,
197        callback: extern "C" fn(*mut aviutl2_sys::plugin2::PROJECT_FILE),
198    ) {
199        self.assert_not_killed();
200        unsafe {
201            ((*self.internal).register_project_load_handler)(callback);
202        }
203    }
204
205    /// プロジェクトファイルを保存する直前に呼ばれる関数を登録します。
206    ///
207    /// # Note
208    ///
209    /// [`crate::generic::GenericPlugin::on_project_save`] が自動的に登録されるため、
210    /// 通常はこの関数を直接使用する必要はありません。
211    pub fn register_project_save_handler(
212        &mut self,
213        callback: extern "C" fn(*mut aviutl2_sys::plugin2::PROJECT_FILE),
214    ) {
215        self.assert_not_killed();
216        unsafe {
217            ((*self.internal).register_project_save_handler)(callback);
218        }
219    }
220}
221
222/// 汎用プラグインのメニュー登録用トレイト。
223pub trait GenericPluginMenus {
224    fn register_menus(host: &mut HostAppHandle);
225}
226
227#[doc(inline)]
228pub use aviutl2_macros::generic_menus as menus;
229
230#[derive(Default)]
231pub(crate) struct PluginRegistry {
232    #[cfg(feature = "input")]
233    input_plugins: Vec<std::sync::Arc<InternalReferenceHandle>>,
234    #[cfg(feature = "output")]
235    output_plugins: Vec<std::sync::Arc<InternalReferenceHandle>>,
236    #[cfg(feature = "filter")]
237    filter_plugins: Vec<std::sync::Arc<InternalReferenceHandle>>,
238    #[cfg(feature = "module")]
239    script_modules: Vec<std::sync::Arc<InternalReferenceHandle>>,
240}
241impl PluginRegistry {
242    pub(crate) fn new() -> Self {
243        Self::default()
244    }
245}
246
247macro_rules! impl_plugin_registry {
248    (
249        $description:literal,
250        $feature:literal,
251        $module:ident,
252        $name:ident,
253        $register_method:ident,
254        $PluginTrait:path,
255        $SingletonTrait:path,
256        $TableType:ty
257    ) => {
258        paste! {
259            impl<T> SubPlugin<T> {
260                #[cfg(feature = $feature)]
261                #[doc = concat!($description, "の新しいインスタンスを作成します。")]
262                pub fn [<new_ $name>](info: AviUtl2Info) -> crate::AnyResult<Self>
263                where
264                    T: $PluginTrait + $SingletonTrait + 'static
265                {
266                    crate::$module::__bridge::initialize_plugin::<T>(info.version.into())?;
267                    let internal = std::sync::Arc::new(InternalReferenceHandle {
268                        uninitialize_fn: || {
269                            unsafe {
270                                crate::$module::__bridge::uninitialize_plugin::<T>();
271                            }
272                        },
273                    });
274                    Ok(Self {
275                        plugin: std::marker::PhantomData,
276                        internal,
277                    })
278                }
279            }
280            #[cfg(feature = $feature)]
281            impl<'a> HostAppHandle<'a> {
282                #[doc = concat!($description, "を登録します。")]
283                pub fn [<register_ $name>]<T: $PluginTrait + $SingletonTrait + 'static>(
284                    &mut self,
285                    handle: &SubPlugin<T>,
286                ) {
287                    self.assert_not_killed();
288                    unsafe { ((*self.internal).$register_method)(crate::$module::__bridge::create_table::<T>()) };
289                    self.plugin_registry
290                        .[<$name s>]
291                        .push(std::sync::Arc::clone(&handle.internal));
292                }
293            }
294        }
295    };
296}
297
298impl_plugin_registry!(
299    "入力プラグイン",
300    "input",
301    input,
302    input_plugin,
303    register_input_plugin,
304    crate::input::InputPlugin,
305    crate::input::__bridge::InputSingleton,
306    aviutl2_sys::input2::INPUT_PLUGIN_TABLE
307);
308impl_plugin_registry!(
309    "出力プラグイン",
310    "output",
311    output,
312    output_plugin,
313    register_output_plugin,
314    crate::output::OutputPlugin,
315    crate::output::__bridge::OutputSingleton,
316    aviutl2_sys::output2::OUTPUT_PLUGIN_TABLE
317);
318impl_plugin_registry!(
319    "フィルタープラグイン",
320    "filter",
321    filter,
322    filter_plugin,
323    register_filter_plugin,
324    crate::filter::FilterPlugin,
325    crate::filter::__bridge::FilterSingleton,
326    aviutl2_sys::filter2::FILTER_PLUGIN_TABLE
327);
328impl_plugin_registry!(
329    "スクリプトモジュール",
330    "module",
331    module,
332    script_module,
333    register_script_module,
334    crate::module::ScriptModule,
335    crate::module::__bridge::ScriptModuleSingleton,
336    aviutl2_sys::module2::SCRIPT_MODULE_TABLE
337);
338
339/// 編集ハンドル。
340#[derive(Debug)]
341pub struct EditHandle {
342    pub(crate) internal: *mut aviutl2_sys::plugin2::EDIT_HANDLE,
343}
344
345unsafe impl Send for EditHandle {}
346unsafe impl Sync for EditHandle {}
347
348/// [`EditHandle`] 関連のエラー。
349#[derive(thiserror::Error, Debug)]
350pub enum EditHandleError {
351    #[error("api call failed")]
352    ApiCallFailed,
353}
354
355impl EditHandle {
356    pub(crate) unsafe fn new(internal: *mut aviutl2_sys::plugin2::EDIT_HANDLE) -> Self {
357        Self { internal }
358    }
359
360    /// プロジェクトデータの編集を開始します。
361    ///
362    /// # Note
363    ///
364    /// 内部では call_edit_section_param を使用しています。
365    pub fn call_edit_section<'a, T, F>(&self, callback: F) -> Result<T, EditHandleError>
366    where
367        T: Send + 'static,
368        F: FnOnce(&mut EditSection) -> T + Send + 'a,
369    {
370        type CallbackParam<'a, F, T> = (ChildKillablePointer<Option<F>>, &'a mut Option<T>);
371
372        let closure = Some(callback);
373        let param = KillablePointer::new(closure);
374        let child_param = param.create_child();
375
376        extern "C" fn trampoline<F, T>(
377            param: *mut std::ffi::c_void,
378            edit_section: *mut aviutl2_sys::plugin2::EDIT_SECTION,
379        ) where
380            T: Send + 'static,
381            F: FnOnce(&mut EditSection) -> T,
382        {
383            unsafe {
384                let (child_param, result_ptr) = &mut *(param as *mut CallbackParam<F, T>);
385                let callback = child_param
386                    .as_mut()
387                    .take()
388                    .expect("Callback has already been called");
389                let mut edit_section = EditSection::from_ptr(edit_section);
390                let res = callback(&mut edit_section);
391
392                result_ptr.replace(res);
393            }
394        }
395
396        let trampoline_static = trampoline::<F, T>
397            as extern "C" fn(*mut std::ffi::c_void, *mut aviutl2_sys::plugin2::EDIT_SECTION);
398
399        let mut result = None;
400        let param = Box::<CallbackParam<F, T>>::new((child_param, &mut result));
401        let param_ptr = Box::into_raw(param);
402
403        let success = unsafe {
404            ((*self.internal).call_edit_section_param)(
405                param_ptr as *mut std::ffi::c_void,
406                trampoline_static,
407            )
408        };
409
410        if success {
411            Ok(result.expect("Callback did not set result"))
412        } else {
413            Err(EditHandleError::ApiCallFailed)
414        }
415    }
416}
417
418struct KillablePointer<T> {
419    kill_switch: std::sync::Arc<std::sync::atomic::AtomicBool>,
420    inner: *mut T,
421}
422unsafe impl<T> Send for KillablePointer<T> {}
423unsafe impl<T> Sync for KillablePointer<T> {}
424impl<T> Drop for KillablePointer<T> {
425    fn drop(&mut self) {
426        self.kill_switch
427            .store(true, std::sync::atomic::Ordering::SeqCst);
428    }
429}
430impl<T> KillablePointer<T> {
431    pub fn new(inner: T) -> Self {
432        Self {
433            kill_switch: std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)),
434            inner: Box::into_raw(Box::new(inner)),
435        }
436    }
437
438    pub fn create_child(&self) -> ChildKillablePointer<T> {
439        ChildKillablePointer {
440            kill_switch: std::sync::Arc::clone(&self.kill_switch),
441            inner: self.inner,
442        }
443    }
444}
445
446struct ChildKillablePointer<T> {
447    kill_switch: std::sync::Arc<std::sync::atomic::AtomicBool>,
448    inner: *mut T,
449}
450unsafe impl<T> Send for ChildKillablePointer<T> {}
451unsafe impl<T> Sync for ChildKillablePointer<T> {}
452impl<T> ChildKillablePointer<T> {
453    pub fn is_killed(&self) -> bool {
454        self.kill_switch.load(std::sync::atomic::Ordering::SeqCst)
455    }
456
457    pub unsafe fn as_mut(&mut self) -> &mut T {
458        if self.is_killed() {
459            panic!("parent KillablePointer has been dropped");
460        }
461        unsafe { &mut *self.inner }
462    }
463}